package com.gitee.apanlh.util.reflection;

import com.gitee.apanlh.util.base.CollUtils;
import com.gitee.apanlh.util.base.Empty;
import com.gitee.apanlh.util.base.StringUtils;
import com.gitee.apanlh.util.cache.local.Cache;
import com.gitee.apanlh.util.cache.local.CacheUtils;
import com.gitee.apanlh.util.check.CheckImport;
import com.gitee.apanlh.util.check.CheckLibrary;
import com.gitee.apanlh.util.reflection.copy.CopyConfig;
import com.gitee.apanlh.util.valid.ValidParam;
import org.springframework.cglib.beans.BeanCopier;
import org.springframework.cglib.core.Converter;

import java.util.List;

/**	
 * 	Bean拷贝工具类
 * 	
 * 	@author Pan
 */
public class CopyUtils {
	
	static {
		CheckImport.library(CheckLibrary.SPRING_CORE);
	}
	
	private static final Cache<String, BeanCopier> CACHE = CacheUtils.cache(32);
	
	/**
	 * 	构造函数
	 * 
	 * 	@author Pan
	 */
	private CopyUtils() {
		//	不允许外部实例
		super();
	}
	
	/**	
	 * 	单对象拷贝
	 * 
	 * 	@author Pan
	 * 	@param <T>		返回类型
	 * 	@param  source	原对象
	 * 	@param  target	目标类
	 * 	@return	T
	 */
	public static <T> T copy(Object source, Class<T> target) {
		if (source == null) {
			return null;
		}

		BeanCopier createCopier = createCopier(source, target, false);
		return copy(createCopier, source, target, null);
	}
	
	/**	
	 * 	单对象拷贝
	 * 
	 * 	@author Pan
	 * 	@param <T>			返回类型
	 * 	@param  source		原对象
	 * 	@param  target		目标类
	 * 	@param  copyConfig	拷贝配置
	 * 	@return	T
	 */
	public static <T> T copy(Object source, Class<T> target, CopyConfig copyConfig) {
		if (source == null) {
			return null;
		}

		boolean useConverter = ValidParam.isNotNull(copyConfig);
		BeanCopier createCopier = createCopier(source, target, useConverter);
		if (useConverter) {
			copyConfig.setTarget(target);
			return copy(createCopier, source, target, copyConfig.getConverter());
		}
		return copy(createCopier, source, target, null);
	}
	
	/**	
	 * 	集合对象拷贝
	 * 	
	 * 	@author Pan
	 * 	@param  <E>     数据类型
	 * 	@param 	<T>		返回类型
	 * 	@param  source	原对象
	 * 	@param  target	目标类
	 * 	@return	List
	 */
	public static <E, T> List<T> copyList(List<E> source, Class<T> target) {
		return copyList(source, target, null);
	}
	
	/**	
	 * 	集合对象拷贝
	 * 	
	 * 	@author Pan
	 * 	@param 	<E>			数据类型
	 * 	@param 	<T>			返回类型
	 * 	@param  source		原对象
	 * 	@param  target		目标类
	 * 	@param  copyConfig	拷贝配置
	 * 	@return	List
	 */
	public static <E, T> List<T> copyList(List<E> source, Class<T> target, CopyConfig copyConfig) {
		if (source == null) {
			return null;
		}
		if (ValidParam.isEmpty(source)) {
			return Empty.list();
		}
		
		return CollUtils.newArrayList(list -> {
			boolean useConverter = copyConfig != null;
			BeanCopier beanCopier = createCopier(source, target, useConverter);
			if (useConverter) {
				copyConfig.setTarget(target);
				for (int i = 0; i < source.size(); i++) {
					list.add(copy(beanCopier, source.get(i), target, copyConfig.getConverter()));
				}
			} else {
				for (int i = 0; i < source.size(); i++) {
					list.add(copy(beanCopier, source.get(i), target, null));
				}
			}
		}, source.size());
	}
	
	/**	
	 * 	拷贝方法
	 * 	
	 * 	@author Pan
	 * 	@param 	<E>			数据类型
	 * 	@param 	<T>			返回类型
	 * 	@param 	beanCopier	拷贝器
	 * 	@param 	source		原始对象
	 * 	@param 	target		目标类
	 * 	@param 	converter	转换器
	 * 	@return	T
	 */
	private static <E, T> T copy(BeanCopier beanCopier, E source, Class<T> target, Converter converter) {
		T newInstance = ReflectionUtils.newInstance(target);
		beanCopier.copy(source, newInstance, converter);
		return newInstance;
	}
	
	/**	
	 * 	创建	拷贝器
	 * 
	 * 	@author Pan
	 * 	@param 	source			对象
	 * 	@param 	target			目标类
	 * 	@param 	useConverter	true使用转换器
	 * 	@return	BeanCopier
	 */
	private static BeanCopier createCopier(Object source, Class<?> target, boolean useConverter) {
		Class<?> sourceClass = ClassTypeUtils.isCollection(source) ? ClassUtils.getClass((List<?>) source) : source.getClass();
		String key = StringUtils.append(ClassUtils.getSimpleName(source), "#", ClassUtils.getSimpleName(target), ClassConvertUtils.toStr(useConverter));
		return CACHE.get(key, () -> BeanCopier.create(sourceClass, target, useConverter));
	}
}
